/**
 * 
 */
package edu.hit.irlab.coref.evaluation.methds;

import java.util.ArrayList;

import edu.hit.irlab.coref.evaluation.PRScoreStruct;;

/**
 * the Valain evaluate algorithm
 * @author langjun, chuter
 *
 */
public class Valain {
	
	private static int partition(ArrayList<Integer> singleKeyCluster, 
			ArrayList<ArrayList<Integer>> responseSet) {
		int numInKey = singleKeyCluster.size();
		int numCluster = 0;

		ArrayList<Integer> checked = new ArrayList<Integer>();
		checked.clear();
		for(ArrayList<Integer> oneClusterRes : responseSet) {
			int first = 0;
			for(Integer item : singleKeyCluster) {
				for(Integer oneItemCluster : oneClusterRes){
					//if(oneItemCluster == item)
					if(oneItemCluster.equals(item)) {
						checked.add(item);
						if(first == 0) {
							numCluster ++;
							first = 1;
						}
						break;
					}
				}
			}
		}
		return numCluster + numInKey - checked.size();
	}

	public static ValainScore scorePart(
			ArrayList<ArrayList<Integer>> entityMentionsMatchKey,
			ArrayList<ArrayList<Integer>> entityMentionsMatchRes
		) {
		ArrayList<Double> recall = ValainMark(
				entityMentionsMatchKey, 
				entityMentionsMatchRes
			);
		ArrayList<Double> precise = ValainMark(
				entityMentionsMatchRes, 
				entityMentionsMatchKey
			);
		return new ValainScore(
				precise.get(0), 
				precise.get(1),
				recall.get(0),
				recall.get(1)
				);
	}
	
	public static PRScoreStruct scoreRP(
			ArrayList<ArrayList<Integer>> entityMentionsMatchKey,
			ArrayList<ArrayList<Integer>> entityMentionsMatchRes
		) {
		ArrayList<Double> recall = ValainMark(
				entityMentionsMatchKey, 
				entityMentionsMatchRes
			);
		ArrayList<Double> precise = ValainMark(
				entityMentionsMatchRes,
				entityMentionsMatchKey
			);
		
		return new PRScoreStruct(precise.get(2), recall.get(2));
	}
	
	private static ArrayList<Double> ValainMark(ArrayList<ArrayList<Integer>> standardSet, ArrayList<ArrayList<Integer>> responseSet) {
		ArrayList<Double> rtnList = new ArrayList<Double>();
		double sumAbove = 0;
		double sumBlow = 0;

		int size = 0;
		int part = 0;
		for(ArrayList<Integer> oneClusterInStd : standardSet) {
			size = oneClusterInStd.size();
			part = partition(oneClusterInStd, responseSet);
			sumAbove += (size - part);
			sumBlow += (size - 1);
		}
		double tempSumAbove = 0;
		if(sumBlow == 0){
			rtnList.add(tempSumAbove);
			rtnList.add(sumBlow);
			rtnList.add(tempSumAbove);
		}
		else{
			if(sumAbove > sumBlow) {
				System.out.println("SumAbove is larger than SumBlow!");
			}
			rtnList.add(sumAbove);
			rtnList.add(sumBlow);
			rtnList.add(sumAbove/sumBlow);
			
		}
		return rtnList;
	}

	

	public static ArrayList<Double> score(
			ArrayList<ArrayList<Integer>> entityMentionsMatchKey,
			ArrayList<ArrayList<Integer>> entityMentionsMatchRes
		) {
		ArrayList<Double> rtnValainScores = new ArrayList<Double>();
		
		ArrayList<Double> recall = ValainMark(entityMentionsMatchKey, entityMentionsMatchRes);
		ArrayList<Double> precise = ValainMark(entityMentionsMatchRes, entityMentionsMatchKey);
	    double fmeasure = 2 / (1 / recall.get(2) + 1 / precise.get(2));  
		
	    rtnValainScores.add(recall.get(2));
	    rtnValainScores.add(precise.get(2));
	    rtnValainScores.add(fmeasure);
	    rtnValainScores.add(recall.get(0));
	    rtnValainScores.add(recall.get(1));
	    rtnValainScores.add(precise.get(0));
	    rtnValainScores.add(precise.get(1));
		
		return rtnValainScores;
	}

	public static class ValainScore {
		public double pAbove;
		public double pBelow;
		public double rAbove;
		public double rBelow;
		public ValainScore(double pAbove, double pBelow, double rAbove, double rBelow) {
			this.pAbove = pAbove;
			this.pBelow = pBelow;
			this.rAbove = rAbove;
			this.rBelow = rBelow;
		}
		//add the two score_parts
		public void add(ValainScore anotherScore) {
			pAbove += anotherScore.pAbove;
			pBelow += anotherScore.pBelow;
			rAbove += anotherScore.rAbove;
			rBelow += anotherScore.rBelow;
		}
		//convert to PRScore
		public PRScoreStruct toPRScore() {
			//P=pAbove/pBelow, R=rAbove/rBelow
			return new PRScoreStruct(pAbove/pBelow, rAbove/rBelow);
		}
	}

	//test program
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayList<ArrayList<Integer>> standardSet = new ArrayList<ArrayList<Integer>>();
		ArrayList<ArrayList<Integer>> responseSet = new ArrayList<ArrayList<Integer>>();
		ArrayList<ArrayList<Integer>> responseSet2 = new ArrayList<ArrayList<Integer>>();
		ArrayList<Integer> standard1 = new ArrayList<Integer>();
		ArrayList<Integer> standard2 = new ArrayList<Integer>();
		ArrayList<Integer> standard3 = new ArrayList<Integer>();
		ArrayList<Integer> response1 = new ArrayList<Integer>();
		ArrayList<Integer> response2 = new ArrayList<Integer>();
		
		ArrayList<Integer> response12 = new ArrayList<Integer>();
		ArrayList<Integer> response22 = new ArrayList<Integer>();
		
		standard1.add(1);
		standard1.add(2);
		standard1.add(3);
		standard1.add(4);
		standard1.add(5);
		
		standard2.add(6);
		standard2.add(7);
		
		standard3.add(8);
		standard3.add(9);
		standard3.add(10);
		standard3.add(11);
		standard3.add(12);
		
		standardSet.add(standard1);
		standardSet.add(standard2);
		standardSet.add(standard3);
		
		
		response1.add(1);
		response1.add(2);
		response1.add(3);
		response1.add(4);
		response1.add(5);

		response2.add(6);
		response2.add(7);
		
		response2.add(8);
		response2.add(9);
		response2.add(10);
		response2.add(11);
		response2.add(12);
		
		responseSet.add(response1);
		responseSet.add(response2);
		
		response12.add(1);
		response12.add(2);
		response12.add(3);
		response12.add(4);
		response12.add(5);
		response12.add(8);
		response12.add(9);
		response12.add(10);
		response12.add(11);
		response12.add(12);
		
		
		response22.add(6);
		response22.add(7);
		
		responseSet2.add(response12);
		responseSet2.add(response22);
				
		System.out.println(Valain.scorePart(standardSet, responseSet).toPRScore());
		System.out.println(Valain.score(standardSet, responseSet));
		System.out.println(Valain.score(standardSet, responseSet2));
	}
}
